<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Highscore - Boost C++ 库 - 事件处理</title>
<link rel="stylesheet" href="css/highscore.css" type="text/css">
<link rev="made" href="mailto:boris@highscore.de">
<link rel="home" href="frontpage.html" title="Boost C++ 库">
<link rel="up" href="frontpage.html" title="Boost C++ 库">
<link rel="prev" href="functionobjects.html" title="第 3 章 函数对象">
<link rel="next" href="asio.html" title="第 5 章 异步输入输出">
<link rel="chapter" href="introduction.html" title="第 1 章 简介">
<link rel="chapter" href="functionobjects.html" title="第 2 章 函数对象">
<link rel="chapter" href="functionobjects.html" title="第 3 章 函数对象">
<link rel="chapter" href="eventhandling.html" title="第 4 章 事件处理">
<link rel="chapter" href="asio.html" title="第 5 章 异步输入输出">
<link rel="chapter" href="asio.html" title="第 6 章 异步输入输出">
<link rel="chapter" href="asio.html" title="第 7 章 异步输入输出">
<link rel="chapter" href="interprocesscommunication.html" title="第 8 章 进程间通讯">
<link rel="chapter" href="castoperators.html" title="第 9 章 类型转换操作符">
<link rel="chapter" href="castoperators.html" title="第 10 章 类型转换操作符">
<link rel="chapter" href="castoperators.html" title="第 11 章 类型转换操作符">
<link rel="chapter" href="castoperators.html" title="第 12 章 类型转换操作符">
<link rel="chapter" href="castoperators.html" title="第 13 章 类型转换操作符">
<link rel="chapter" href="castoperators.html" title="第 14 章 类型转换操作符">
<link rel="chapter" href="castoperators.html" title="第 15 章 类型转换操作符">
<link rel="chapter" href="castoperators.html" title="第 16 章 类型转换操作符">
<link rel="section" href="eventhandling.html#eventhandling_general" title="4.1. 概述">
<link rel="section" href="eventhandling.html#eventhandling_signals" title="4.2. 信号 Signals">
<link rel="section" href="eventhandling.html#eventhandling_connections" title="4.3. 连接 Connections">
<link rel="section" href="eventhandling.html#eventhandling_exercises" title="4.4. 练习">
<meta http-equiv="pics-label" content='(pics-1.1 "http://www.icra.org/ratingsv02.html" l gen true for "http://www.highscore.de" r (nz 1 vz 1 lz 1 oz 1 cz 1) gen true for "http://highscore.de" r (nz 1 vz 1 lz 1 oz 1 cz 1))'>
<meta http-equiv="Content-Style-Type" content="text/css">
<meta http-equiv="Content-Script-Type" content="text/javascript">
<link href="http://www.highscore.de/favicon.ico" rel="shortcut icon" type="image/vnd.microsoft.icon">
<script type="text/javascript" src="js/jquery-1.3.2.min.js"></script><script type="text/javascript" src="js/jquery.event.drag-1.5.min.js"></script><script type="text/javascript" src="js/highscore.js"></script>
</head>
<body>
<div lang="zh" class="docbook chapter" title="第 4 章 事件处理">
<p class="title">Boost C++ 库</p>
<script type="text/javascript">
          var titlepage = "导言";
        
      var titles = new Array(titlepage,
      
        "第 1 章 简介",
      
        "第 2 章 函数对象",
      
        "第 3 章 函数对象",
      
        "第 4 章 事件处理",
      
        "第 5 章 异步输入输出",
      
        "第 6 章 异步输入输出",
      
        "第 7 章 异步输入输出",
      
        "第 8 章 进程间通讯",
      
        "第 9 章 类型转换操作符",
      
        "第 10 章 类型转换操作符",
      
        "第 11 章 类型转换操作符",
      
        "第 12 章 类型转换操作符",
      
        "第 13 章 类型转换操作符",
      
        "第 14 章 类型转换操作符",
      
        "第 15 章 类型转换操作符",
      
        "第 16 章 类型转换操作符",
      
      "");

      
          var titlehtml = "frontpage.html";
        
      var filenames = new Array(titlehtml,
      
        "introduction.html",
      
        "functionobjects.html",
      
        "functionobjects.html",
      
        "eventhandling.html",
      
        "asio.html",
      
        "asio.html",
      
        "asio.html",
      
        "interprocesscommunication.html",
      
        "castoperators.html",
      
        "castoperators.html",
      
        "castoperators.html",
      
        "castoperators.html",
      
        "castoperators.html",
      
        "castoperators.html",
      
        "castoperators.html",
      
        "castoperators.html",
      
      "");

      
      document.open();
      document.write('<form action="" class="toc">');
      document.write('<select size="1" onchange="location.href=options[selectedIndex].value">');
      for (var i = 0; i < titles.length && i < filenames.length; ++i) {
        if (titles[i] != "" && filenames[i] != "") {
          document.write('<option');
          document.write(' value="' + filenames[i] + '"');
          var expr = new RegExp('[/\]' + filenames[i] + '$');
          if (expr.test(location.href)) {
            document.write(' selected="selected"');
          }
          document.write('>' + titles[i] + '<\/option>');
        }
      }
      document.write('<\/select>');
      document.write('<\/form>');
      document.close();
      
    </script><noscript><p class="toc"><a href="toc.html">目录</a></p></noscript>
<hr class="hrhead">
<h1 class="title">
<a name="eventhandling"></a>第 4 章 事件处理</h1>
<hr>
<div class="toc">
<h3>目录</h3>
<ul>
<li><span class="sect1"><a href="eventhandling.html#eventhandling_general">4.1 概述</a></span></li>
<li><span class="sect1"><a href="eventhandling.html#eventhandling_signals">4.2 信号 Signals</a></span></li>
<li><span class="sect1"><a href="eventhandling.html#eventhandling_connections">4.3 连接 Connections</a></span></li>
<li><span class="sect1"><a href="eventhandling.html#eventhandling_exercises">4.4 练习</a></span></li>
</ul>
</div>
<p class="license"><a href="http://creativecommons.org/licenses/by-nc-nd/3.0/de/deed.zh" rel="license" target="_top"><img src="img/88x31_cc_logo.gif" alt="" width="88" height="31"></a> 该书采用 <a href="http://creativecommons.org/licenses/by-nc-nd/3.0/de/deed.zh" rel="license" target="_top">Creative Commons License</a> 授权</p>
<hr>
<h2 class="title">
<a name="eventhandling_general"></a>4.1. 概述</h2>
<div class="sect1">
<p>很多开发者在听到术语'事件处理'时就会想到GUI：点击一下某个按钮，相关联的功能就会被执行。
    点击本身就是事件，而功能就是相对应的事件处理器。</p>
<p>这一模式的使用当然不仅限于GUI。 一般情况下，任意对象都可以调用基于特定事件的专门函数。 本章所介绍的 <a class="link" href="http://www.boost.org/libs/signals">Boost.Signals</a>
    库提供了一个简单的方法在 C++ 中应用这一模式。</p>
<p>严格来说，Boost.Function 库也可以用于事件处理。 不过，Boost.Function 和 Boost.Signals
    之间的一个主要区别在于，Boost.Signals 能够将一个以上的事件处理器关联至单个事件。 因此，Boost.Signals
    可以更好地支持事件驱动的开发，当需要进行事件处理时，应作为第一选择。</p>
</div>
<hr>
<h2 class="title">
<a name="eventhandling_signals"></a>4.2. 信号 Signals</h2>
<div class="sect1">
<p>虽然这个库的名字乍一看好象有点误导，但实际上并非如此。 Boost.Signals 所实现的模式被命名为 '信号至插槽' (signal
    to slot)，它基于以下概念：当对应的信号被发出时，相关联的插槽即被执行。 原则上，你可以把单词 '信号' 和 '插槽' 分别替换为 '事件'
    和 '事件处理器'。 不过，由于信号可以在任意给定的时间发出，所以这一概念放弃了 '事件' 的名字。</p>
<p>因此，Boost.Signals 没有提供任何类似于 '事件' 的类。 相反，它提供了一个名为
    <code class="classname">boost::signal</code> 的类，定义于 <code class="filename">boost/signal.hpp</code>.
    实际上，这个头文件是唯一一个需要知道的，因为它会自动包含其它相关的头文件。</p>
<p>Boost.Signals 定义了其它一些类，位于 <span class="package">boost::signals</span> 名字空间中。
    由于 <code class="classname">boost::signal</code> 是最常被用到的类，所以它是位于名字空间
    <span class="package">boost</span> 中的。</p>
<pre class="programlisting">#include &lt;boost/signal.hpp&gt; 
#include &lt;iostream&gt; 

void func() 
{ 
  std::cout &lt;&lt; "Hello, world!" &lt;&lt; std::endl; 
} 

int main() 
{ 
  boost::signal&lt;void ()&gt; s; 
  s.connect(func); 
  s(); 
} </pre>
<ul class="programlisting"><li><a class="programlisting" href="src/4.2.1/main.cpp">下载源代码</a></li></ul>
<p><code class="classname">boost::signal</code>
    实际上被实现为一个模板函数，具有被用作为事件处理器的函数的签名，该签名也是它的模板参数。 在这个例子中，只有签名为 <code class="code">void
    ()</code> 的函数可以被成功关联至信号 <var>s</var>。</p>
<p>函数 <code class="function">func()</code> 被通过 <code class="methodname">connect()</code>
    方法关联至信号 <var>s</var>。 由于 <code class="function">func()</code> 符合所要求的
    <code class="code">void ()</code> 签名，所以该关联成功建立。因此当信号 <var>s</var>
    被触发时，<code class="function">func()</code> 将被调用。</p>
<p>信号是通过调用 <var>s</var> 来触发的，就象普通的函数调用那样。
    这个函数的签名对应于作为模板参数传入的签名：因为 <code class="code">void ()</code> 不要求任何参数，所以括号内是空的。</p>
<p>调用 <var>s</var> 会引发一个触发器，进而执行相应的 <code class="function">func()</code>
    函数 - 之前用 <code class="methodname">connect()</code> 关联了的。</p>
<p>同一例子也可以用 Boost.Function 来实现。</p>
<pre class="programlisting">#include &lt;boost/function.hpp&gt; 
#include &lt;iostream&gt; 

void func() 
{ 
  std::cout &lt;&lt; "Hello, world!" &lt;&lt; std::endl; 
} 

int main() 
{ 
  boost::function&lt;void ()&gt; f; 
  f = func; 
  f(); 
} </pre>
<ul class="programlisting"><li><a class="programlisting" href="src/4.2.2/main.cpp">下载源代码</a></li></ul>
<p>和前一个例子相类似，<code class="function">func()</code> 被关联至 <var>f</var>。 当
    <var>f</var> 被调用时，就会相应地执行 <code class="function">func()</code>。
    Boost.Function 仅限于这种情形下适用，而 Boost.Signals
    则提供了多得多的方式，如关联多个函数至单个特定信号，示例如下。</p>
<pre class="programlisting">#include &lt;boost/signal.hpp&gt; 
#include &lt;iostream&gt; 

void func1() 
{ 
  std::cout &lt;&lt; "Hello" &lt;&lt; std::flush; 
} 

void func2() 
{ 
  std::cout &lt;&lt; ", world!" &lt;&lt; std::endl; 
} 

int main() 
{ 
  boost::signal&lt;void ()&gt; s; 
  s.connect(func1); 
  s.connect(func2); 
  s(); 
} </pre>
<ul class="programlisting"><li><a class="programlisting" href="src/4.2.3/main.cpp">下载源代码</a></li></ul>
<p><code class="classname">boost::signal</code> 可以通过反复调用
    <code class="methodname">connect()</code> 方法来把多个函数赋值给单个特定信号。 当该信号被触发时，这些函数被按照之前用
    <code class="methodname">connect()</code> 进行关联时的顺序来执行。</p>
<p>另外，执行的顺序也可通过 <code class="methodname">connect()</code>
    方法的另一个重载版本来明确指定，该重载版本要求以一个 <code class="type">int</code> 类型的值作为额外的参数。</p>
<pre class="programlisting">#include &lt;boost/signal.hpp&gt; 
#include &lt;iostream&gt; 

void func1() 
{ 
  std::cout &lt;&lt; "Hello" &lt;&lt; std::flush; 
} 

void func2() 
{ 
  std::cout &lt;&lt; ", world!" &lt;&lt; std::endl; 
} 

int main() 
{ 
  boost::signal&lt;void ()&gt; s; 
  s.connect(1, func2); 
  s.connect(0, func1); 
  s(); 
} </pre>
<ul class="programlisting"><li><a class="programlisting" href="src/4.2.4/main.cpp">下载源代码</a></li></ul>
<p>和前一个例子一样，<code class="function">func1()</code> 在 <code class="function">func2()</code>
    之前执行。</p>
<p>要释放某个函数与给定信号的关联，可以用 <code class="methodname">disconnect()</code> 方法。</p>
<pre class="programlisting">#include &lt;boost/signal.hpp&gt; 
#include &lt;iostream&gt; 

void func1() 
{ 
  std::cout &lt;&lt; "Hello" &lt;&lt; std::endl; 
} 

void func2() 
{ 
  std::cout &lt;&lt; ", world!" &lt;&lt; std::endl; 
} 

int main() 
{ 
  boost::signal&lt;void ()&gt; s; 
  s.connect(func1); 
  s.connect(func2); 
  s.disconnect(func2); 
  s(); 
} </pre>
<ul class="programlisting"><li><a class="programlisting" href="src/4.2.5/main.cpp">下载源代码</a></li></ul>
<p>这个例子仅输出 <code class="computeroutput">Hello</code>，因为与
    <code class="function">func2()</code> 的关联在触发信号之前已经被释放。</p>
<p>除了 <code class="methodname">connect()</code> 和
    <code class="methodname">disconnect()</code>
    以外，<code class="classname">boost::signal</code> 还提供了几个方法。</p>
<pre class="programlisting">#include &lt;boost/signal.hpp&gt; 
#include &lt;iostream&gt; 

void func1() 
{ 
  std::cout &lt;&lt; "Hello" &lt;&lt; std::flush; 
} 

void func2() 
{ 
  std::cout &lt;&lt; ", world!" &lt;&lt; std::endl; 
} 

int main() 
{ 
  boost::signal&lt;void ()&gt; s; 
  s.connect(func1); 
  s.connect(func2); 
  std::cout &lt;&lt; s.num_slots() &lt;&lt; std::endl; 
  if (!s.empty()) 
    s(); 
  s.disconnect_all_slots(); 
} </pre>
<ul class="programlisting"><li><a class="programlisting" href="src/4.2.6/main.cpp">下载源代码</a></li></ul>
<p><code class="methodname">num_slots()</code> 返回已关联函数的数量。如果没有函数被关联，则
    <code class="methodname">num_slots()</code> 返回0。 在这种特定情况下，可以用
    <code class="methodname">empty()</code> 方法来替代。
    <code class="methodname">disconnect_all_slots()</code>
    方法所做的实际上正是它的名字所表达的：释放所有已有的关联。</p>
<p>看完了函数如何被关联至信号，以及弄明白了信号被触发时会发生什么事之后，还有一个问题：这些函数的返回值去了哪里？
    以下例子回答了这个问题。</p>
<pre class="programlisting">#include &lt;boost/signal.hpp&gt; 
#include &lt;iostream&gt; 

int func1() 
{ 
  return 1; 
} 

int func2() 
{ 
  return 2; 
} 

int main() 
{ 
  boost::signal&lt;int ()&gt; s; 
  s.connect(func1); 
  s.connect(func2); 
  std::cout &lt;&lt; s() &lt;&lt; std::endl; 
} </pre>
<ul class="programlisting"><li><a class="programlisting" href="src/4.2.7/main.cpp">下载源代码</a></li></ul>
<p><code class="function">func1()</code> 和 <code class="function">func2()</code> 都具有
    <code class="type">int</code> 类型的返回值。 <var>s</var> 将处理两个返回值，并将它们都写出至标准输出流。
    那么，到底会发生什么呢？</p>
<p>以上例子实际上会把 <code class="computeroutput">2</code> 写出至标准输出流。 两个返回值都被
    <var>s</var> 正确接收，但除了最后一个值，其它值都会被忽略。
    缺省情况下，所有被关联函数中，实际上只有最后一个返回值被返回。</p>
<p>你可以定制一个信号，令每个返回值都被相应地处理。 为此，要把一个称为合成器(combiner)的东西作为第二个参数传递给
    <code class="classname">boost::signal</code>。</p>
<pre class="programlisting">#include &lt;boost/signal.hpp&gt; 
#include &lt;iostream&gt; 
#include &lt;algorithm&gt; 

int func1() 
{ 
  return 1; 
} 

int func2() 
{ 
  return 2; 
} 

template &lt;typename T&gt; 
struct min_element 
{ 
  typedef T result_type; 

  template &lt;typename InputIterator&gt; 
  T operator()(InputIterator first, InputIterator last) const 
  { 
    return *std::min_element(first, last); 
  } 
}; 

int main() 
{ 
  boost::signal&lt;int (), min_element&lt;int&gt; &gt; s; 
  s.connect(func1); 
  s.connect(func2); 
  std::cout &lt;&lt; s() &lt;&lt; std::endl; 
} </pre>
<ul class="programlisting"><li><a class="programlisting" href="src/4.2.8/main.cpp">下载源代码</a></li></ul>
<p>合成器是一个重载了 <code class="methodname">operator()()</code>
    操作符的类。这个操作符会被自动调用，传入两个迭代器，指向某个特定信号的所有返回值。 以上例子使用了标准 C++ 算法
    <code class="function">std::min_element()</code> 来确定并返回最小的值。</p>
<p>不幸的是，我们不可能把象 <code class="function">std::min_element()</code> 这样的一个算法直接传给
    <code class="classname">boost::signal</code> 作为一个模板参数。
    <code class="classname">boost::signal</code> 要求这个合成器定义一个名为
    <code class="type">result_type</code> 的类型，用于说明 <code class="methodname">operator()()</code>
    操作符返回值的类型。 由于在标准 C++ 算法中缺少这个类型，所以在编译时会产生一个相应的错误。</p>
<p>除了对返回值进行分析以外，合成器也可以保存它们。</p>
<pre class="programlisting">#include &lt;boost/signal.hpp&gt; 
#include &lt;iostream&gt; 
#include &lt;vector&gt; 
#include &lt;algorithm&gt; 

int func1() 
{ 
  return 1; 
} 

int func2() 
{ 
  return 2; 
} 

template &lt;typename T&gt; 
struct min_element 
{ 
  typedef T result_type; 

  template &lt;typename InputIterator&gt; 
  T operator()(InputIterator first, InputIterator last) const 
  { 
    return T(first, last); 
  } 
}; 

int main() 
{ 
  boost::signal&lt;int (), min_element&lt;std::vector&lt;int&gt; &gt; &gt; s; 
  s.connect(func1); 
  s.connect(func2); 
  std::vector&lt;int&gt; v = s(); 
  std::cout &lt;&lt; *std::min_element(v.begin(), v.end()) &lt;&lt; std::endl; 
} </pre>
<ul class="programlisting"><li><a class="programlisting" href="src/4.2.9/main.cpp">下载源代码</a></li></ul>
<p>这个例子把所有返回值保存在一个 vector 中，再由 <code class="code">s()</code> 返回。</p>
</div>
<hr>
<h2 class="title">
<a name="eventhandling_connections"></a>4.3. 连接 Connections</h2>
<div class="sect1">
<p>函数可以通过由 <code class="classname">boost::signal</code> 所提供的
    <code class="methodname">connect()</code> 和 <code class="methodname">disconnect()</code>
    方法的帮助来进行管理。 由于 <code class="methodname">connect()</code> 会返回一个类型为
    <code class="classname">boost::signals::connection</code> 的值，它们可以通过其它方法来管理。</p>
<pre class="programlisting">#include &lt;boost/signal.hpp&gt; 
#include &lt;iostream&gt; 

void func() 
{ 
  std::cout &lt;&lt; "Hello, world!" &lt;&lt; std::endl; 
} 

int main() 
{ 
  boost::signal&lt;void ()&gt; s; 
  boost::signals::connection c = s.connect(func); 
  s(); 
  c.disconnect(); 
} </pre>
<ul class="programlisting"><li><a class="programlisting" href="src/4.3.1/main.cpp">下载源代码</a></li></ul>
<p><code class="classname">boost::signal</code> 的
    <code class="methodname">disconnect()</code> 方法需要传入一个函数指针，而直接调用
    <code class="classname">boost::signals::connection</code> 对象上的
    <code class="methodname">disconnect()</code> 方法则略去该参数。</p>
<p>除了 <code class="methodname">disconnect()</code>
    方法之外，<code class="classname">boost::signals::connection</code> 还提供了其它方法，如
    <code class="methodname">block()</code> 和 <code class="methodname">unblock()</code>。</p>
<pre class="programlisting">#include &lt;boost/signal.hpp&gt; 
#include &lt;iostream&gt; 

void func() 
{ 
  std::cout &lt;&lt; "Hello, world!" &lt;&lt; std::endl; 
} 

int main() 
{ 
  boost::signal&lt;void ()&gt; s; 
  boost::signals::connection c = s.connect(func); 
  c.block(); 
  s(); 
  c.unblock(); 
  s(); 
} </pre>
<ul class="programlisting"><li><a class="programlisting" href="src/4.3.2/main.cpp">下载源代码</a></li></ul>
<p>以上程序只会执行一次 <code class="function">func()</code>。 虽然信号 <var>s</var>
    被触发了两次，但是在第一次触发时 <code class="function">func()</code> 不会被调用，因为连接 <var>c</var>
    实际上已经被 <code class="methodname">block()</code> 调用所阻塞。 由于在第二次触发之前调用了
    <code class="methodname">unblock()</code>，所以之后 <code class="function">func()</code>
    被正确地执行。</p>
<p>除了 <code class="classname">boost::signals::connection</code> 以外，还有一个名为
    <code class="classname">boost::signals::scoped_connection</code>
    的类，它会在析构时自动释放连接。</p>
<pre class="programlisting">#include &lt;boost/signal.hpp&gt; 
#include &lt;iostream&gt; 

void func() 
{ 
  std::cout &lt;&lt; "Hello, world!" &lt;&lt; std::endl; 
} 

int main() 
{ 
  boost::signal&lt;void ()&gt; s; 
  { 
    boost::signals::scoped_connection c = s.connect(func); 
  } 
  s(); 
} </pre>
<ul class="programlisting"><li><a class="programlisting" href="src/4.3.3/main.cpp">下载源代码</a></li></ul>
<p>因为连接对象 <var>c</var> 在信号触发之前被销毁，所以 <code class="function">func()</code>
    不会被调用。</p>
<p><code class="classname">boost::signals::scoped_connection</code> 实际上是派生自
    <code class="classname">boost::signals::connection</code>
    的，所以它提供了相同的方法。它们之间的区别仅在于，在析构
    <code class="classname">boost::signals::scoped_connection</code> 时，连接会自动释放。</p>
<p>虽然 <code class="classname">boost::signals::scoped_connection</code>
    的确令自动释放连接更为容易，但是该类型的对象仍需要管理。 如果在其它情形下连接也可以被自动释放，而且不需要管理这些对象的话，就更好了。</p>
<pre class="programlisting">#include &lt;boost/signal.hpp&gt; 
#include &lt;boost/bind.hpp&gt; 
#include &lt;iostream&gt; 
#include &lt;memory&gt; 

class world 
{ 
  public: 
    void hello() const 
    { 
      std::cout &lt;&lt; "Hello, world!" &lt;&lt; std::endl; 
    } 
}; 

int main() 
{ 
  boost::signal&lt;void ()&gt; s; 
  { 
    std::auto_ptr&lt;world&gt; w(new world()); 
    s.connect(boost::bind(&amp;world::hello, w.get())); 
  } 
  std::cout &lt;&lt; s.num_slots() &lt;&lt; std::endl; 
  s(); 
} </pre>
<ul class="programlisting"><li><a class="programlisting" href="src/4.3.4/main.cpp">下载源代码</a></li></ul>
<p>以上程序使用 Boost.Bind 将一个对象的方法关联至一个信号。 在信号触发之前，这个对象就被销毁了，这会产生问题。
    我们不传递实际的对象 <var>w</var>，而只传递一个指针给
    <code class="function">boost::bind()</code>。 在 <code class="code">s()</code>
    被实际调用的时候，该指针所引向的对象已不再存在。</p>
<p>可以如下修改这个程序，使得一旦对象 <var>w</var> 被销毁，连接就会自动释放。</p>
<pre class="programlisting">#include &lt;boost/signal.hpp&gt; 
#include &lt;boost/bind.hpp&gt; 
#include &lt;iostream&gt; 
#include &lt;memory&gt; 

class world : 
  public boost::signals::trackable 
{ 
  public: 
    void hello() const 
    { 
      std::cout &lt;&lt; "Hello, world!" &lt;&lt; std::endl; 
    } 
}; 

int main() 
{ 
  boost::signal&lt;void ()&gt; s; 
  { 
    std::auto_ptr&lt;world&gt; w(new world()); 
    s.connect(boost::bind(&amp;world::hello, w.get())); 
  } 
  std::cout &lt;&lt; s.num_slots() &lt;&lt; std::endl; 
  s(); 
} </pre>
<ul class="programlisting"><li><a class="programlisting" href="src/4.3.5/main.cpp">下载源代码</a></li></ul>
<p>如果现在再执行，<code class="methodname">num_slots()</code> 会返回
    <code class="computeroutput">0</code> 以确保不会试图调用已销毁对象之上的方法。 仅需的修改是让
    <code class="classname">world</code> 类继承自
    <code class="classname">boost::signals::trackable</code>。
    当使用对象的指针而不是对象的副本来关联函数至信号时，<code class="classname">boost::signals::trackable</code>
    可以显著简化连接的管理。</p>
</div>
<hr>
<h2 class="title">
<a name="eventhandling_exercises"></a>4.4. 练习</h2>
<div class="sect1">
<p class="solution">
              You can buy 
              <a target="_top" href="http://en.highscore.de/shop/index.php?p=boost-solution">solutions to all exercises</a>
              in this book as a ZIP file. 
            </p>
<ol><li class="listitem">
<p>编写一个程序，定义一个名为 <code class="classname">button</code> 的类，表示GUI中的一个可点击按钮。
        为该类加入两个方法 <code class="function">add_handler()</code> 和
        <code class="function">remove_handler()</code>，它们均要求一个函数名作为参数。 如果
        <code class="methodname">click()</code> 方法被调用，已登记的函数将被按顺序执行。</p>
<p>如下测试你的代码，创建一个 <code class="classname">button</code>
        类的实例，从事件处理器内部向标准输出流写出一个信息。 调用 <code class="function">click()</code>
        函数模拟用鼠标点击该按钮。</p>
</li></ol>
</div>
</div>
<hr class="hrfoot">
<p class="copyright">版权 © 2008-2010 
        <a class="link" href="mailto:boris@highscore.de">Boris Schäling</a>
      </p>
</body>
</html>
